﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using System.IO;

namespace knn
{
    public partial class Form1 : Form
    {

        private const string FilesError = "please select files";
        private const string NieghborError = "please insert number of nighbors";
        private const string ReadingTrain = "reading train...";
        private const string Proccessing = "proccessing ...";
        private const string Finish = "finish..";

        public Form1()
        {
            InitializeComponent();
        }

        private void OpenFileDialog1FileOk(object sender, CancelEventArgs e)
        {
            txbTraining.Text = openFileDialog1.FileName;
        }

        private void BtnBrowseTrainingClick(object sender, EventArgs e)
        {
            openFileDialog1.ShowDialog();
        }

        private void BtnBrowseTestingClick(object sender, EventArgs e)
        {
            openFileDialog2.ShowDialog();
        }

        private void OpenFileDialog2FileOk(object sender, CancelEventArgs e)
        {
            txbTesting.Text = openFileDialog2.FileName;
        }

        private void BrnRunClick(object sender, EventArgs e)
        {
            txbMessage.Visible = false;
            if (txbTesting.Text.Length == 0 || txbTraining.Text.Length == 0)
            {
                txbMessage.Text = FilesError;
                txbMessage.Visible = true;
                Application.DoEvents();
            }
            else if (nudNeighbors.Value <= 0)
            {
                txbMessage.Text = NieghborError;
                txbMessage.Visible = true;
                Application.DoEvents();
            }
            else
            {
                if (rbnDiscrete.Checked)
                {
                    DiscreteKnn();
                }

                if (rbnContinuous.Checked)
                {
                    ContinuousKnn();
                }
            }
        }

        private static double[] Populate(IList<string> data)
        {
            double[] res = new double[data.Count-1];
            for (int i = 0; i < res.Length; i++)
            {
                res[i] = Double.Parse(data[i]);
            }
            return res;
        }

        private void ReadTrain(IDictionary<double[], string> neighbors)
        {
            StreamReader sr = new StreamReader(txbTraining.Text);
            string line;
            while ((line = sr.ReadLine()) != null && (line.Length > 1))
            {
                string[] data = line.Split(',');
                double[] neighbor = Populate(data);
                neighbors.Add(neighbor, data[data.Length-1]);
            }

            sr.Close();
        }

        private static double CalcEuclideanDistance(IEnumerable<double> candiate, IList<double> neighbor)
        {
            double res = candiate.Select((t, i) => ((t - neighbor[i])*(t - neighbor[i]))).Sum();
            return Math.Sqrt(res);
        }

        private void DiscreteKnn()
        {
            Dictionary<double[], string> neighbors = new Dictionary<double[], string>();

            txbMessage.Text = ReadingTrain;
            txbMessage.Visible = true;
            Application.DoEvents();
            ReadTrain(neighbors);
            
            
            StreamReader testReader = new StreamReader(txbTesting.Text);
            StreamWriter output = new StreamWriter("result.txt", false);
            string line;
            

            txbMessage.Text = Proccessing;
            Application.DoEvents();

            while ((line = testReader.ReadLine()) != null)
            {
                Dictionary<string, int> classificationCount = new Dictionary<string, int>
                    {{"KK", 0}, {"Ger", 0}, {"Kolt", 0}, {"Rez", 0}};
                SortedList<double, string> results = new SortedList<double, string>();

                string[] data = line.Split(',');
                double[] candidate = Populate(data);

                foreach (KeyValuePair<double[], string> neighbor in neighbors)
                {
                    double euclidean = CalcEuclideanDistance(candidate, neighbor.Key);
                    results.Add(euclidean, neighbor.Value);
                }

                for (int i = 0; i < (int)nudNeighbors.Value && i < results.Count; i++)
                {
                    classificationCount[results.ElementAt(i).Value]++;
                }

                classificationCount = classificationCount.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
                output.WriteLine(classificationCount.First().Key);

            }
            testReader.Close();
            output.Close();
            txbMessage.Text = Finish;
            Application.DoEvents();
        }

        private void ReadTrain(IDictionary<double[], double> neighbors)
        {
            StreamReader sr = new StreamReader(txbTraining.Text);
            string line;
            while ((line = sr.ReadLine()) != null && (line.Length > 1))
            {
                string[] data = line.Split(',');
                double[] neighbor = Populate(data);
                neighbors.Add(neighbor, double.Parse(data[data.Length-1]));
            }

            sr.Close();
        }

        private void ContinuousKnn()
        {
            Dictionary<double[], double> neighbors = new Dictionary<double[], double>();

            txbMessage.Text = ReadingTrain;
            txbMessage.Visible = true;
            Application.DoEvents();
            ReadTrain(neighbors);

            StreamReader testReader = new StreamReader(txbTesting.Text);
            StreamWriter output = new StreamWriter("result.txt", false);
            string line;
            

            txbMessage.Text = Proccessing;
            Application.DoEvents();

            while ((line = testReader.ReadLine()) != null)
            {
                string[] data = line.Split(',');
                double[] candidate = Populate(data);
                SortedList<double, double> results = new SortedList<double, double>();

                foreach (KeyValuePair<double[], double> neighbor in neighbors)
                {
                    double euclidean = CalcEuclideanDistance(candidate, neighbor.Key);
                    results.Add(euclidean, neighbor.Value);
                }

                double avg = 0.0;

                for (int i = 0; i < (int)nudNeighbors.Value && i < results.Count; i++)
                {
                    avg += results.ElementAt(i).Value;
                }

                avg /= (int) nudNeighbors.Value;


                output.WriteLine(avg);

            }
            testReader.Close();
            output.Close();
            txbMessage.Text = Finish;
            Application.DoEvents();

        }

        
    }
}
